package java_thinking.unit_17._17_9_10;

import java_thinking.unit_17._17_2_6.Countries;

import java.util.AbstractMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static java_thinking.net.mindview.util.Print.print;

/**
 * @Desc:   用每个Map中的next来存储下一个Map，就不用list了，也就不用iterator来遍历每个桶下的Map了
 * @author: cww
 * @DateTime: 2019-04-20 10:37
 */

public class SimpleHashMap5<K,V> extends AbstractMap<K,V> {
    static class Entry<K,V> implements Map.Entry<K,V> {
        private K key;
        private V value;
        Entry<K,V> next;

        public Entry(K key, V value) {
            this.key = key;
            this.value = value;
        }
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        @Override
        public V setValue(V v) {
            V result = value;
            value = v;
            return result;
        }

        @Override
        public int hashCode() {
            return key.hashCode() ^ (value == null ? 0 : value.hashCode());
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof Entry) {
                Entry<K,V> e = (Entry<K,V>)o;
                Object key1 = getKey();
                Object key2 = e.getValue();
                if (key1.equals(key2)) {
                    Object value1 = getValue();
                    Object value2 = e.getValue();
                    //好写法！
                    return value1 == null ? value2 == null : value1.equals(value2);
                }
            }
            return false;
        }
        @Override
        public String toString() {
            return key + "=" + value;
        }

    }

    static final int SIZE = 997;

    @SuppressWarnings("unchecked")
    Entry<K,V>[] buckets = new Entry[SIZE];

    @Override
    public V put(K key, V value) {
        V oldValue = null;
        int index = Math.abs(key.hashCode()) % SIZE;
        Entry<K,V> newPair = new Entry<K,V>(key,value);
        if (buckets[index] == null) {
            buckets[index] = newPair;
        }
        Entry<K,V> prevPair = null;
        boolean found = false;
        for (Entry<K,V> pair = buckets[index]; pair != null; pair = pair.next) {
            if (pair.getKey().equals(key)) {
                oldValue = pair.getValue();
                if (prevPair != null) {
                    prevPair.next = newPair;
                } else {
                    buckets[index] = newPair;
                }
                newPair.next = pair.next;
                found = true;
                break;
            }
            prevPair = pair;
        }
        if (!found) {
            prevPair.next = newPair;
        }
        return oldValue;
    }

    @Override
    public V get(Object key) {
        int index = Math.abs(key.hashCode()) % SIZE;
        for (Entry<K,V> pair = buckets[index]; pair != null; pair = pair.next) {
            if (pair.getKey().equals(key)) {
                return pair.getValue();
            }
        }
        return null;
    }

    @Override
    public Set<Map.Entry<K, V>> entrySet() {
        Set<Map.Entry<K,V>> set = new HashSet<>();
        for (Entry<K,V> bucket : buckets) {
            for (Entry<K,V> pair = bucket; pair != null; pair = pair.next) {
                set.add(pair);
            }
        }
        return set;
    }
    @Override
    @SuppressWarnings("unchecked")
    public void clear() {
        // Effectively erase everything by allocating
        // a new empty array of buckets:
        buckets = new Entry[SIZE];
    }

    @Override
    public V remove(Object key) {
        int index = Math.abs(key.hashCode()) % SIZE;
        Entry<K,V> prePair = null;
        for (Entry<K,V> pair = buckets[index]; pair != null; pair = pair.next) {
            if (pair.getKey().equals(key)) {
                V value = pair.getValue();
                if (prePair != null) {
                    prePair.next = pair.next;
                } else {
//                    buckets[index] =null;
                    if (pair.next != null) {
                        buckets[index] = pair.next;
                    } else {
                        buckets[index] = null;
                    }
                }
                return value;
            }
            prePair = pair;
        }
        return null;
    }

    @Override
    public int size() {
        int sz = 0;
        for (Entry<K,V> bucket : buckets) {
            for (Entry<K,V> pair = bucket; pair != null; pair = pair.next) {
                sz ++;
            }
        }
        return sz;
    }

    @Override
    public boolean isEmpty() {
        for (Entry<K,V> bucket : buckets) {
            if (bucket != null) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean containsKey(Object key) {
        for (Entry<K,V> bucket : buckets) {
            for (Entry<K,V> pair = bucket; pair !=  null; pair = pair.next) {
                if (pair.getKey().equals(key)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static void main(String[] args) {
        SimpleHashMap5<String,String> m = new SimpleHashMap5<String,String>(),
                m2 = new SimpleHashMap5<String,String>();
        m.putAll(Countries.capitals(10));
        m2.putAll(Countries.capitals(10));
        print("m.size() = " + m.size());
        print("m.isEmpty() = " + m.isEmpty());
        print("m.equals(m2) = " + m.equals(m2));
        print("m.containsKey(\"BENIN\") = " + m.containsKey("BENIN"));
        print("m.containsKey(\"MARS\") = " + m.containsKey("MARS"));
        print("m.keySet() = " + m.keySet());
        print("m.values() = " + m.values());
        print("m = " + m);
        m.remove("ANGOLA");
        print("m = " + m);
    }
}
